//! ARM GICv3 (Generic Interrupt Controller version 3) driver
//!
//! This implementation is based on the Linux kernel GICv3 driver and the
//! ARM GICv3 Architecture Specification.

#![no_std]

pub mod regs;

use core::ptr::{read_volatile, write_volatile};
pub use regs::*;

/// The maximum number of IRQs supported by GICv3
const MAX_IRQ_COUNT: usize = 1024;

/// Default PMR (Priority Mask Register) value
const DEFAULT_PMR_VALUE: u32 = 0xf0;

/// Default interrupt priority
const GICD_INT_DEF_PRI: u8 = 0xa0;

/// GICv3 Distributor
pub struct GicDistributor {
    base: *mut u8,
}

unsafe impl Send for GicDistributor {}
unsafe impl Sync for GicDistributor {}

/// GICv3 Redistributor
pub struct GicRedistributor {
    rd_base: *mut u8,
    sgi_base: *mut u8,
}

unsafe impl Send for GicRedistributor {}
unsafe impl Sync for GicRedistributor {}

/// GICv3 CPU Interface (accessed via system registers)
pub struct GicCpuInterface;

impl GicDistributor {
    /// Creates a new GIC distributor instance
    ///
    /// # Safety
    /// The caller must ensure that the base address is valid and points to
    /// the GIC distributor's MMIO region.
    pub const unsafe fn new(base: *mut u8) -> Self {
        Self { base }
    }

    /// Reads a 32-bit value from the distributor at the given offset
    #[inline]
    fn read32(&self, offset: usize) -> u32 {
        unsafe { read_volatile(self.base.add(offset) as *const u32) }
    }

    /// Writes a 32-bit value to the distributor at the given offset
    #[inline]
    fn write32(&self, offset: usize, value: u32) {
        unsafe { write_volatile(self.base.add(offset) as *mut u32, value) }
    }

    /// Waits for the distributor to complete a register write
    fn wait_for_rwp(&self) {
        let mut count = 1_000_000; // 1s timeout
        while (self.read32(GICD_CTLR) & GICD_CTLR_RWP) != 0 {
            count -= 1;
            if count == 0 {
                // Timeout
                return;
            }
            core::hint::spin_loop();
        }
    }

    /// Initializes the distributor
    pub fn init(&mut self) {
        // Disable the distributor
        self.write32(GICD_CTLR, 0);
        self.wait_for_rwp();

        // Get the number of interrupt lines
        let typer = self.read32(GICD_TYPER);
        let nr_lines = ((typer & 0x1f) + 1) * 32;

        // Configure SPIs as non-secure Group-1
        for i in (32..nr_lines).step_by(32) {
            self.write32(GICD_IGROUPR + (i / 8) as usize, 0xffffffff);
        }

        // Disable all SPIs
        for i in (32..nr_lines).step_by(32) {
            self.write32(GICD_ICENABLER + (i / 8) as usize, 0xffffffff);
        }

        // Clear all pending SPIs
        for i in (32..nr_lines).step_by(32) {
            self.write32(GICD_ICPENDR + (i / 8) as usize, 0xffffffff);
        }

        // Set default priority for all SPIs
        for i in 32..nr_lines {
            self.write32(GICD_IPRIORITYR + i as usize, GICD_INT_DEF_PRI as u32);
        }

        // Configure all SPIs as level-triggered (default)
        for i in (32..nr_lines).step_by(16) {
            self.write32(GICD_ICFGR + (i / 4) as usize, 0);
        }

        // Enable distributor with Affinity Routing
        self.write32(
            GICD_CTLR,
            GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1,
        );
        self.wait_for_rwp();
    }

    /// Enables or disables an interrupt
    pub fn set_enable(&mut self, irq_num: u32, enabled: bool) {
        let reg_offset = if enabled {
            GICD_ISENABLER
        } else {
            GICD_ICENABLER
        };
        let index = (irq_num / 32) as usize;
        let bit = irq_num % 32;

        self.write32(reg_offset + index * 4, 1 << bit);
        self.wait_for_rwp();
    }

    /// Sets the priority of an interrupt
    pub fn set_priority(&mut self, irq_num: u32, priority: u8) {
        let offset = GICD_IPRIORITYR + irq_num as usize;
        self.write32(offset, priority as u32);
    }

    /// Sets the target CPU(s) for an SPI
    pub fn set_spi_route(&mut self, irq_num: u32, affinity: u64) {
        if irq_num >= 32 {
            // IROUTER is 64-bit wide
            let offset = GICD_IROUTER + (irq_num as usize * 8);
            unsafe {
                write_volatile(self.base.add(offset) as *mut u64, affinity);
            }
        }
    }
}

impl GicRedistributor {
    /// Creates a new GIC redistributor instance
    ///
    /// # Safety
    /// The caller must ensure that the base addresses are valid.
    pub const unsafe fn new(rd_base: *mut u8) -> Self {
        // SGI base is 64KB after the RD_base
        let sgi_base = rd_base.add(0x10000);
        Self { rd_base, sgi_base }
    }

    /// Reads a 32-bit value from the redistributor at the given offset
    #[inline]
    fn read32_rd(&self, offset: usize) -> u32 {
        unsafe { read_volatile(self.rd_base.add(offset) as *const u32) }
    }

    /// Writes a 32-bit value to the redistributor at the given offset
    #[inline]
    fn write32_rd(&self, offset: usize, value: u32) {
        unsafe { write_volatile(self.rd_base.add(offset) as *mut u32, value) }
    }

    /// Reads a 32-bit value from the SGI base at the given offset
    #[inline]
    fn read32_sgi(&self, offset: usize) -> u32 {
        unsafe { read_volatile(self.sgi_base.add(offset) as *const u32) }
    }

    /// Writes a 32-bit value to the SGI base at the given offset
    #[inline]
    fn write32_sgi(&self, offset: usize, value: u32) {
        unsafe { write_volatile(self.sgi_base.add(offset) as *mut u32, value) }
    }

    /// Waits for the redistributor to complete a register write
    fn wait_for_rwp(&self) {
        let mut count = 1_000_000; // 1s timeout
        while (self.read32_rd(GICR_CTLR) & GICD_CTLR_RWP) != 0 {
            count -= 1;
            if count == 0 {
                return;
            }
            core::hint::spin_loop();
        }
    }

    /// Wakes up the redistributor
    pub fn wake(&mut self) {
        let mut val = self.read32_rd(GICR_WAKER);
        val &= !GICR_WAKER_ProcessorSleep;
        self.write32_rd(GICR_WAKER, val);

        // Wait for ChildrenAsleep to clear
        let mut count = 1_000_000;
        loop {
            val = self.read32_rd(GICR_WAKER);
            if (val & GICR_WAKER_ChildrenAsleep) == 0 {
                break;
            }
            count -= 1;
            if count == 0 {
                break;
            }
            core::hint::spin_loop();
        }
    }

    /// Initializes the redistributor for the current CPU
    pub fn init(&mut self) {
        // Wake up the redistributor
        self.wake();

        // Configure SGIs and PPIs as Group-1
        self.write32_sgi(GICR_IGROUPR0, 0xffffffff);

        // Disable all SGIs and PPIs
        self.write32_sgi(GICR_ICENABLER0, 0xffffffff);
        self.wait_for_rwp();

        // Clear all pending SGIs and PPIs
        self.write32_sgi(GICR_ICPENDR0, 0xffffffff);

        // Set default priority for all SGIs and PPIs
        for i in 0..32 {
            self.write32_sgi(GICR_IPRIORITYR0 + i, GICD_INT_DEF_PRI as u32);
        }
    }

    /// Enables or disables a PPI/SGI
    pub fn set_enable(&mut self, irq_num: u32, enabled: bool) {
        if irq_num < 32 {
            let bit = 1 << irq_num;
            if enabled {
                self.write32_sgi(GICR_ISENABLER0, bit);
            } else {
                self.write32_sgi(GICR_ICENABLER0, bit);
            }
            self.wait_for_rwp();
        }
    }
}

impl GicCpuInterface {
    /// Creates a new CPU interface instance
    pub const fn new() -> Self {
        Self
    }

    /// Initializes the CPU interface
    pub fn init(&self) {
        // Set priority mask to allow all interrupts
        Self::write_pmr(DEFAULT_PMR_VALUE);

        // Enable Group 1 interrupts
        Self::write_ctlr(ICC_CTLR_EL1_EOImode_drop);

        // Enable signaling of interrupts
        Self::write_igrpen1(1);
    }

    /// Reads the Interrupt Acknowledge Register (IAR)
    #[inline]
    pub fn read_iar() -> u32 {
        let val: u64;
        unsafe {
            core::arch::asm!("mrs {}, ICC_IAR1_EL1", out(reg) val);
        }
        val as u32
    }

    /// Writes to the End of Interrupt Register (EOIR)
    #[inline]
    pub fn write_eoir(irq: u32) {
        unsafe {
            core::arch::asm!("msr ICC_EOIR1_EL1, {}", in(reg) irq as u64);
        }
    }

    /// Writes to the Priority Mask Register (PMR)
    #[inline]
    fn write_pmr(val: u32) {
        unsafe {
            core::arch::asm!("msr ICC_PMR_EL1, {}", in(reg) val as u64);
        }
    }

    /// Writes to the Control Register (CTLR)
    #[inline]
    fn write_ctlr(val: u32) {
        unsafe {
            core::arch::asm!("msr ICC_CTLR_EL1, {}", in(reg) val as u64);
        }
    }

    /// Writes to the Interrupt Group Enable Register (IGRPEN1)
    #[inline]
    fn write_igrpen1(val: u32) {
        unsafe {
            core::arch::asm!("msr ICC_IGRPEN1_EL1, {}", in(reg) val as u64);
        }
    }

    /// Handles an IRQ by calling the provided handler
    pub fn handle_irq<F>(&self, mut handler: F)
    where
        F: FnMut(u32),
    {
        loop {
            let irq = Self::read_iar();

            // Check for spurious interrupt (1023) or special IDs
            if irq >= 1020 {
                break;
            }

            // Call the handler
            handler(irq);

            // Signal end of interrupt
            Self::write_eoir(irq);
        }
    }
}

impl Default for GicCpuInterface {
    fn default() -> Self {
        Self::new()
    }
}
